<template>
  <!-- 图表容器 -->

  <div class="chart-wrapper">
    <!-- 使用vue-echarts组件渲染雷达图 -->
    <!-- option: 图表配置选项，包含所有图表设置 -->
    <!-- autoresize: 布尔值，当窗口大小改变时自动调整图表大小 -->
    <!-- style: 内联样式，设置图表高度为100% -->
    <v-chart
      ref="chartRef"
      :option="option"
      autoresize
      :update-options="{ notMerge: false }"
      style="height: 100%;"
    />
  </div>
</template>

<script setup>
// 从vue导入ref、onMounted和onUnmounted函数
// ref: 创建响应式引用
// onMounted: 组件挂载完成后执行的生命周期钩子
// onUnmounted: 组件卸载前执行的生命周期钩子
import { ref, onMounted, onUnmounted } from 'vue'
// 导入vue-echarts组件，用于渲染ECharts图表
import VChart from 'vue-echarts'
// 从echarts核心模块导入基础功能
import * as echarts from 'echarts/core'
// 1. 引入雷达图图表类型
import { RadarChart } from 'echarts/charts'
// 2. 引入必要组件
// TitleComponent: 标题组件，用于显示图表标题
// LegendComponent: 图例组件，显示不同数据系列的标识
// RadarComponent: 雷达图组件，提供雷达图的坐标系
// TooltipComponent: 提示框组件，鼠标悬停时显示信息
import {
  TitleComponent,
  LegendComponent,
  RadarComponent,
  TooltipComponent
} from 'echarts/components'
// CanvasRenderer: Canvas渲染器，用于渲染图表
import { CanvasRenderer } from 'echarts/renderers'

// 3. 注册使用的组件和图表类型到ECharts核心模块
// TitleComponent: 标题组件
// LegendComponent: 图例组件
// RadarComponent: 雷达图坐标系组件
// TooltipComponent: 提示框组件
// RadarChart: 雷达图图表类型
// CanvasRenderer: Canvas渲染器
echarts.use([
  TitleComponent,
  LegendComponent,
  RadarComponent,
  TooltipComponent,
  RadarChart,   // ← 关键的雷达图图表类型
  CanvasRenderer
])

// 创建响应式引用option，用于存储图表配置选项
// 初始值为空对象，将在组件挂载后填充具体配置
const option = ref({})
// 创建响应式引用，用于存储图表实例
const chartRef = ref(null)
// 创建响应式引用，用于存储原始数据
const rawData = ref([])

/**
 * 归一化函数，将数据映射到指定范围内（支持设置最小值）
 * @param {Array} list - 需要处理的数据列表
 * @param {Function} getter - 获取数据值的函数
 * @param {Number} maxRaw - 原始数据的最大值，用于归一化计算
 * @param {Number} minRaw - 原始数据的最小值，用于归一化计算（可选，默认为0）
 * @param {Number} targetMin - 目标范围的最小值（可选，默认为0）
 * @param {Number} targetMax - 目标范围的最大值（可选，默认为100）
 * @returns {Array} 归一化后的数据数组，值在[targetMin, targetMax]之间
 */
function normalize(list, getter, maxRaw, minRaw = 0, targetMin = 0, targetMax = 100) {
  // 计算目标范围的差值
  const targetRange = targetMax - targetMin;
  // 计算原始数据范围的差值
  const rawRange = maxRaw - minRaw;

  // 遍历数据列表，对每个元素进行归一化处理
  // getter(it): 获取当前元素的值
  // Math.max(minRaw, getter(it)): 确保值不低于最小值
  // (Math.max(minRaw, getter(it)) - minRaw): 将值调整为从0开始
  // ((Math.max(minRaw, getter(it)) - minRaw) / rawRange): 归一化到0-1范围
  // ((Math.max(minRaw, getter(it)) - minRaw) / rawRange) * targetRange: 映射到目标范围
  // targetMin + (((Math.max(minRaw, getter(it)) - minRaw) / rawRange) * targetRange): 调整到目标范围
  // Math.min(targetMax, ...): 确保最大值不超过目标最大值
  // Math.max(targetMin, ...): 确保最小值不低于目标最小值
  return list.map(it =>
    Math.min(targetMax,
      Math.max(targetMin,
        targetMin + (((Math.max(minRaw, getter(it)) - minRaw) / rawRange) * targetRange)
      )
    )
  );
}

/**
 * 格式化票房数值显示
 * @param {Number} value - 票房数值
 * @returns {String} 格式化后的字符串
 */
function formatRevenue(value) {
  // 将归一化后的值转换回原始值范围进行显示
  if (value >= 1e12) {
    return (value / 1e12).toFixed(2) + '万亿';
  } else if (value >= 1e8) {
    return (value / 1e8).toFixed(2) + '亿';
  } else {
    return value.toFixed(0);
  }
}

/**
 * 格式化不同维度的数值显示
 * @param {Number} value - 数值
 * @param {Number} index - 维度索引
 * @returns {String} 格式化后的字符串
 */
function formatValue(value, index) {
  switch(index) {
    case 0: // 均分
      return value.toFixed(2);
    case 1: // 票房
      return formatRevenue(value);
    case 2: // 热度
      return value.toFixed(2);
    case 3: // 产出
      return value.toFixed(0) + '部';
    default:
      return value.toFixed(2);
  }
}

/**
 * 获取指标名称
 * @param {Number} index - 指标索引
 * @returns {String} 指标名称
 */
function getIndicatorName(index) {
  const names = ['均分', '票房', '热度', '产出'];
  return names[index] || '';
}

// 在组件挂载完成后执行的异步函数
onMounted(async () => {
  // 发起网络请求获取Top10公司数据
  // fetch: 浏览器原生API，用于发起HTTP请求
  // '/api/queryTop10Company': 请求的API地址
  const resp = await fetch('/api/queryTop5Company')
  // 检查响应是否成功，如果不成功则抛出错误
  // ok: Response对象的属性，表示响应是否成功（状态码200-299）
  if (!resp.ok) throw new Error('fetch radar data error')
  // 解析响应数据为JSON格式
  // json(): 异步方法，将响应体解析为JSON对象
  const list = await resp.json() // 10 条公司数据

  // 保存原始数据
  rawData.value = list;

  /* 归一化 4 维数据 */
  // 对公司平均评分进行归一化处理，设置最小值为5，最大值为7.5
  // normalize(list, v => v.avgVote, 7.5, 5): 使用箭头函数获取每个公司的平均评分并归一化，最小值为5
  const voteArr   = normalize(list, v => v.avgVote, 6.5, 5)            // 满分 10，最小分5

  // 对公司总票房进行归一化处理，设置最小值为1e12（1万亿），最大值为4e12（4万亿）
  // normalize(list, v => v.sumRevenue, 4e12, 1e12): 使用箭头函数获取每个公司的总票房并归一化
  const revArr    = normalize(list, v => v.sumRevenue, 3.7e12, 2.5e12)       // 1万亿到4万亿

  // 对公司平均热度进行归一化处理，设置最小值为3，最大值为9
  // normalize(list, v => v.avgPopularity, 9, 3): 使用箭头函数获取每个公司的平均热度并归一化
  const popArr    = normalize(list, v => v.avgPopularity, 8.3, 0)      // 3到9热度值

  // 对公司电影产出数量进行归一化处理，设置最小值为500，最大值为2500部
  // normalize(list, v => v.movieCount, 2500, 500): 使用箭头函数获取每个公司的电影数量并归一化
  const countArr  = normalize(list, v => v.movieCount, 2500, 1500)       // 500到2500部

  /* 组装雷达图数据 */
  // 将处理后的数据组装成雷达图所需格式
  // list.map((it, idx) => (...)): 遍历公司列表，idx为索引
  const radarData = list.map((it, idx) => ({
    // name: 公司名称，显示在图例中
    name: it.company,
    // value: 数据值数组，包含四个维度的归一化数据
    // [voteArr[idx], revArr[idx], popArr[idx], countArr[idx]]:
    // 分别对应均分、票房、热度、产出四个维度
    value: [voteArr[idx], revArr[idx], popArr[idx], countArr[idx]],
    // 原始分数，用于悬停显示
    rawScores: [it.avgVote, it.sumRevenue, it.avgPopularity, it.movieCount]
  }))

  // 设置图表配置选项
  option.value = {
    // title: 图表标题配置
    title: {
      // text: 标题文本内容
      text: '票房 Top 5 公司综合雷达',
      textStyle: {
        // fontSize: 字体大小
        fontSize: 25
      },
      // left: 标题水平位置，'center'表示居中显示
      left: 'center',
    },
    // tooltip: 提示框配置
    tooltip: {
      // trigger: 触发类型，'item'表示数据项图形触发
      trigger: 'item',
      // formatter: 提示框内容格式器
      formatter: (params) => {
        // params: 包含数据项信息的对象
        // params.name: 数据项名称（公司名）
        // params.dataIndex: 数据项索引
        const company = list[params.dataIndex];
        const scores = [company.avgVote, company.sumRevenue, company.avgPopularity, company.movieCount];

        // 构建提示框内容
        let tooltipContent = `${params.name}<br/>`;
        for (let i = 0; i < scores.length; i++) {
          tooltipContent += `${getIndicatorName(i)}: ${formatValue(scores[i], i)}<br/>`;
        }
        return tooltipContent;
      }
    },
    // legend: 图例配置
    legend: {
      // orient: 图例方向，'horizontal'表示水平方向
      orient: 'vertical',
      // left: 图例水平位置，'center'表示居中显示
      top: 400,
      right: 125,
      // data: 图例数据，使用公司名称列表
      data: list.map(it => it.company),
      // bottom: 图距离容器底部的距离，单位为像素
      bottom: 10,
      // type: 图例类型，'scroll'表示可滚动的图例（当图例项过多时）
      type: 'scroll',
      backgroundColor: 'rgba(255, 255, 255, 0.8)',  // 白色半透明背景
      // 图例内边距
      padding: [10, 15],  // 上下10px，左右15px
      // 图例边框圆角
      borderRadius: 5,    // 5像素圆角
      // 图例边框样式
      borderColor: '#ccc',  // 边框颜色
      borderWidth: 1        // 边框宽
    },
    // radar: 雷达图坐标系配置
    radar: {
      // indicator: 雷达图指示器配置，定义各个维度的名称和最大值
      indicator: [
        // name: 维度名称，max: 该维度的最大值
        { name: '均分', max: 100 },
        { name: '票房', max: 100 },
        { name: '热度', max: 100 },
        { name: '产出', max: 100 }
      ],
      // shape: 雷达图绘制形状，'polygon'表示多边形
      shape: 'polygon',
      // splitNumber: 指示器轴的分割段数
      splitNumber: 5,
      // name: 指示器名称配置
      name: {
        // textStyle: 文本样式配置
        textStyle: {
          fontSize: 14,      // 字号
          color: '#000',     // 黑色
        }
      },
      radius: 225,
    },
    // series: 图表系列配置，定义实际要绘制的数据
    series: [
      {
        // name: 系列名称
        name: '公司对比',
        // type: 图表类型，'radar'表示雷达图
        type: 'radar',
        // data: 系列数据，使用之前组装的雷达图数据
        data: radarData,
        // color: 图例颜色
        color: ['#f4a7a9','#8bcd8b','#ffcc80','#7ea9e0','#c9a58d'],
        // symbol: 标记点图形，'circle'表示圆形
        symbol: 'circle',
        // symbolSize: 标记点大小
        symbolSize: 8,
        // lineStyle: 线条样式（保持原有样式）
        lineStyle: {
          // width: 线条宽度
          width: 2
        },
      }
    ]
  }
})

// 在组件卸载前执行的函数
onUnmounted(() => {
  // 清理相关资源
})
</script>

<style scoped>
/* 图表容器样式 */
.chart-wrapper {
  /* 使用flex布局 */
  display: flex;
  margin-top: -5vh;
  margin-left: -3vw;
  /* 图表高度设置为550像素 */
  height: 650px;
  /* 宽度占满容器 */
  width: 100%;
  /* 最大宽度设置为视口宽度的80% */
  max-width: 80vw;
}
</style>